package com.vois.jack.btmgr.devices.XZXDevice;

import android.os.Bundle;
import android.os.Message;

import com.vois.jack.btmgr.classicbase.BtDevCommonMsg;
import com.vois.jack.btmgr.classicbase.BtDevConstant;
import com.vois.jack.btmgr.classicbase.BtOpusCodedRecorder;
import com.vois.jack.btmgr.classicbase.BtRecorderInterface;
import com.vois.jack.btmgr.classicbase.DefaultSppBtDevice;
import com.vois.jack.btmgr.common.DeviceRecorderControlInterface;
import com.vois.jack.btmgr.common.IBtConstants;

import java.util.UUID;

public class XZXBtDevice extends DefaultSppBtDevice {
    private int curCommand;
    private int curContentLen;
    private int curContentOffset;
    private short curCheckSum;
    private int curContentType;
    private byte[] curContentData;
    private int curContentMaxBufLen;

    static private final int COMMAND_TYPE = 0x01;
    static private final int AUDIO_TYPE = 0x02;

    static private final int KEY_DOWN   = 0x01;
    static private final int KEY_UP     = 0x02;
    static private final int KEY_PRESS  = 0x11;

    private BtOpusCodedRecorder btOpusCodedRecorder;

    @Override
    public void openDevice() {
        super.openDevice();
        btOpusCodedRecorder = new BtOpusCodedRecorder();
    }

    @Override
    public void closeDevice() {
        super.closeDevice();
        if (btOpusCodedRecorder != null) {
            btOpusCodedRecorder.stopRecord();
            btOpusCodedRecorder.release();
            btOpusCodedRecorder = null;
        }
    }

    @Override
    protected void onSppSocketConnecting() {
        super.onSppSocketConnecting();
        curContentMaxBufLen = 1024;
        curContentData = new byte[curContentMaxBufLen];
    }

    @Override
    protected void onSppSocketDisconnected() {
        super.onSppSocketDisconnected();
        curContentData = null;
    }

    @Override
    protected int processRecvInitState(byte[] data, int dataSize, int curPos) {
        while (data != null && (curPos + 5 <= dataSize) || (curPos + 3 <= dataSize)) {
            if ((curPos + 3 <= dataSize) && (data[curPos] == (byte)0xAA && data[curPos + 1] == (byte)0xBB)) {
                // audio type
                //logger.d("audio type");
                curCommand = 0xEE;
                curContentLen = (data[curPos + 2] & 0xFF);
                curContentType = AUDIO_TYPE;
                curContentOffset = 0;
                if (curContentLen > 0) {
                    if (curContentMaxBufLen < curContentLen) {
                        curContentMaxBufLen = curContentLen;
                        curContentData = new byte[curContentLen];
                    }
                    setRecvState(false);
                }

                return curPos + 3;
            }else if ((curPos + 5 <= dataSize) && (data[curPos] == (byte)0xA4 && data[curPos + 1] == (byte)0xB4)) {
                // command type
                //logger.d("command type");
                curCommand = (data[curPos + 2] & 0xFF);
                curContentLen = (data[curPos + 3] & 0xFF) + 2; // 2 for checksum bytes count
                curCheckSum = 0;
                curCheckSum += (data[curPos + 2] & 0xFF);
                curCheckSum += (data[curPos + 3] & 0xFF);
                curContentType = COMMAND_TYPE;
                if (curContentLen > 0) {
                    curContentOffset = 0;
                    if (curContentMaxBufLen < curContentLen) {
                        curContentMaxBufLen = curContentLen;
                        curContentData = new byte[curContentLen];
                    }
                    setRecvState(false);
                }

                return curPos + 5;
            }

            curPos++;
        }

        return curPos;
    }


    private void processCommand() {
        //logger.d("processCommand curCommand:" + curCommand);
        switch (curCommand) {
            case 0xFF: {
                // key process
                int keyCode = BtDevConstant.BT_KEY_CODE_UNKNOWN;
                int keyStatus = curContentData[1] & 0xFF;
                switch (curContentData[0] & 0xFF) {
                    case 0x40: {
                        keyCode = BtDevConstant.BT_KEY_CODE_PTT;
                    }
                    break;

                    case 0x41: {
                        keyCode = BtDevConstant.BT_KEY_CODE_PREV;
                    }
                    break;

                    case 0x42: {
                        keyCode = BtDevConstant.BT_KEY_CODE_NEXT;
                    }
                    break;

                    case 0x43: {
                        keyCode = BtDevConstant.BT_KEY_CODE_VOLUME_UP;
                    }
                    break;

                    case 0x44: {
                        keyCode = BtDevConstant.BT_KEY_CODE_VOLUME_DOWN;
                    }
                    break;

                    case 0x45: {
                        keyCode = BtDevConstant.BT_KEY_CODE_ANCHOR;
                    }
                    break;
                }

                if (getListener() != null) {
                    Message message = Message.obtain();
                    Bundle bundle = new Bundle();
                    message.setData(bundle);
                    if (keyStatus == KEY_DOWN) {
                        message.what = BtDevCommonMsg.BT_DEV_COMMON_KEY_PRESSED.getValue();
                    }else if (keyStatus == KEY_UP) {
                        message.what = BtDevCommonMsg.BT_DEV_COMMON_KEY_RELEASED.getValue();
                    }else if (keyStatus == KEY_PRESS) {
                        message.what = BtDevCommonMsg.BT_DEV_COMMON_KEY_CLICKED.getValue();
                    }
                    bundle.putInt(BtDevConstant.EXTRA_KEY_CODE, keyCode);
                    getListener().onMessage(this, message);
                }
            }
            break;

            case 0x10: {
                // app information responce
            }
            break;

            case 0x21: {
                // get seqid responce
                String seqUUID;
                int count = curContentLen - 2;
                char[] out = new char[count * 2];
                final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
                        'E', 'F' };
                if (count >= 16) {
                    for (int i = 0, j = 0; i < count; i++) {
                        out[j++] = DIGITS_UPPER[(0xF0 & curContentData[i]) >>> 4];
                        out[j++] = DIGITS_UPPER[0x0F & curContentData[i]];
                    }
                    seqUUID = new String(out);
                }else {
                    seqUUID = "0";
                }

                //TODO: notify seq uuid to user
                if (getListener() != null) {
                    Message message = Message.obtain();
                    message.what = BtDevCommonMsg.BT_DEV_GET_VALIDATE_DATA_RET.getValue();
                    Bundle bundle = new Bundle();
                    bundle.putString(BtDevConstant.EXTRA_SEQID, seqUUID);
                    message.setData(bundle);
                    getListener().onMessage(this, message);
                }
            }
            break;

            case 0x31: {
                // set seqid responce
                int result = curContentData[0] & 0xFF;

                //TODO: notify seq id written result;
                if (getListener() != null) {
                    Message message = Message.obtain();
                    message.what = BtDevCommonMsg.BT_DEV_SET_SEQID_RET.getValue();
                    Bundle bundle = new Bundle();
                    bundle.putInt(BtDevConstant.EXTRA_RET_VALUE, result);
                    message.setData(bundle);
                    getListener().onMessage(this, message);
                }
            }
            break;

            case 0x22: {
                // get vendor responce
                int count = curContentLen - 2;
                byte[] vendorBytes = new byte[count];
                System.arraycopy(curContentData, 0, vendorBytes, 0, count);
                String vendor = new String(vendorBytes);
                if (getListener() != null) {
                    Message message = Message.obtain();
                    message.what = BtDevCommonMsg.BT_DEV_GET_VENDOR_RET.getValue();
                    Bundle bundle = new Bundle();
                    bundle.putString(BtDevConstant.EXTRA_VENDOR, vendor);
                    message.setData(bundle);
                    getListener().onMessage(this, message);
                }
            }
            break;

            case 0x23: {
                // get version responce
                int count = curContentLen - 2;
                byte[] versionBytes = new byte[count];
                System.arraycopy(curContentData, 0, versionBytes, 0, count);
                String version = new String(versionBytes);

                if (getListener() != null) {
                    Message message = Message.obtain();
                    message.what = BtDevCommonMsg.BT_DEV_GET_VERSION_RET.getValue();
                    Bundle bundle = new Bundle();
                    bundle.putString(BtDevConstant.EXTRA_VERSION, version);
                    message.setData(bundle);
                    getListener().onMessage(this, message);
                }
            }
            break;

            case 0x2B: {
                // record indication
            }
            break;

            case 0x34: {
                // start record responce
                int result = curContentData[0] & 0xFF;
                //TODO: notify seq id written result;
                if (getListener() != null) {
                    Message message = Message.obtain();
                    message.what = BtDevCommonMsg.BT_DEV_RECORDER_STARTED_RET.getValue();
                    Bundle bundle = new Bundle();
                    bundle.putInt(BtDevConstant.EXTRA_RET_VALUE, result);
                    message.setData(bundle);
                    getListener().onMessage(this, message);
                }
            }
            break;

            case 0x35: {
                // stop record responce
                int result = curContentData[0] & 0xFF;

                //TODO: notify seq id written result;
                if (getListener() != null) {
                    Message message = Message.obtain();
                    message.what = BtDevCommonMsg.BT_DEV_RECORDER_STOPPED_RET.getValue();
                    Bundle bundle = new Bundle();
                    bundle.putInt(BtDevConstant.EXTRA_RET_VALUE, result);
                    message.setData(bundle);
                    getListener().onMessage(this, message);
                }
            }
            break;

            case 0xEE: {
                //audio incoming
                //logger.dumpArray(curContentData, curContentLen);
                if (btOpusCodedRecorder != null && curContentData != null) {
                    btOpusCodedRecorder.putEncodedRecordData(curContentData, 0, curContentLen);
                }
            }
            break;
        }
    }


    @Override
    protected int processRecvPayloadState(byte[] data, int dataSize, int curPos) {
        while ((curPos < dataSize) && (curContentData != null)) {
            curContentData[curContentOffset] = data[curPos++];
            if (curContentOffset + 2 < curContentLen) {
                curCheckSum += (curContentData[curContentOffset] & 0xFF);
            }
            curContentOffset++;
            if (curContentOffset == curContentLen) {
                //logger.d("----------------constent----------------:" + curContentLen);
                //logger.dumpArray(curContentData, curContentLen);
                if (curContentType == COMMAND_TYPE) {
                    short checkSumGet = (short)(curContentData[curContentLen - 2] & 0xFF);
                    checkSumGet = (short)((checkSumGet << 8) + (curContentData[curContentLen - 1] & 0xFF));
                    if (checkSumGet == curCheckSum) {
                        processCommand();
                    }
                }else {
                    processCommand();
                }

                setRecvState(true);
                curContentLen = 0;
                curContentOffset = 0;
                curCommand = 0;
                break;
            }
        }
        return curPos;
    }

    @Override
    protected UUID getSppConnectUUID() {
        return UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    }

    @Override
    public BtRecorderInterface getRecorder(int recorderType) {
        return btOpusCodedRecorder;
    }

    @Override
    public String getDeviceType() {
        return "xzx device";
    }

    @Override
    public String getProtocolName() {
        return IBtConstants.PROTOCOL_VOIS;
    }

    private void sendCommand(int cmd, byte[] data, int dataSize) {
        byte[] sendData = new byte[dataSize + 7];
        short checksum = 0;

        sendData[0] = (byte)0xA4;
        sendData[1] = (byte)0xB4;
        sendData[2] = (byte)cmd;
        sendData[3] = (byte)(dataSize & 0xFF);

        if (data != null) {
            System.arraycopy(data, 0, sendData, 5, dataSize);
        }

        for (int i = 2; i < 5 + dataSize; i ++) {
            checksum += (sendData[i] & 0xFF);
        }
        sendData[5 + dataSize] = (byte)(checksum & 0xFF);
        sendData[5 + dataSize + 1] = (byte)((checksum & 0xFF00) >> 8);

        //logger.dumpArray(sendData, sendData.length);
        putSppSendData(sendData, sendData.length);
    }

    @Override
    public int getDefaultRecorderType() {
        return OPUS_RECORDER;
    }

    @Override
    public void getValidateData() {
        sendCommand(0x21, null, 0);
    }

    @Override
    public void getVersion() {
        sendCommand(0x23, null, 0);
    }

    @Override
    public void getVendor() {
        sendCommand(0x22, null, 0);
    }

    @Override
    public boolean startRecorder(ResultCallback callback) {
        sendCommand(0x34, null, 0);
        return true;
    }

    @Override
    public void stopRecorder(ResultCallback callback) {
        sendCommand(0x35, null, 0);
    }

    @Override
    public boolean useOwnPlayTone() {
        return true;
    }

    @Override
    public boolean isNeedHeartbeat() {
        return false;
    }

    @Override
    public void setSeqID(String seqID) {
        byte[] content = new byte[16];
        for (int i = 0; i < 16; i++) {
            content[i] = Byte.valueOf(seqID.substring(i * 2, i * 2 + 1));
        }

        sendCommand(0x31, content, 16);
    }
}
